#!/bin/bash
set -e

# ========================================================
# 🚀 Go 服务启动脚本（增强错误处理和自动回滚）
# ========================================================

# ----------------------------
# 基础配置
# ----------------------------
ROOT_DIR="/home/project/bin"        # 根目录
BACKUP_DIR="/home/project/backups"  # 备份目录
APP_NAME="example"                  # 二进制文件名
APP_CONF="$ROOT_DIR/config.yaml"    # 配置文件路径
APP_BIN="$ROOT_DIR/$APP_NAME"       # 二进制路径
PID_FILE="$ROOT_DIR/$APP_NAME.pid"  # PID 文件
LOG_FILE="$ROOT_DIR/$APP_NAME.log"  # 日志文件

# 回滚配置
MAX_BACKUPS=5                       # 最大备份数量
HEALTH_CHECK_TIMEOUT=30             # 健康检查超时时间（秒）
HEALTH_CHECK_INTERVAL=3             # 健康检查间隔（秒）

# ----------------------------
# 颜色输出函数
# ----------------------------
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

color_echo() {
    local color=$1
    shift
    echo -e "${color}$*${NC}"
}

# ----------------------------
# 日志函数
# ----------------------------
log_with_time() {
    while IFS= read -r line || [ -n "$line" ]; do
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line"
    done
}

log_info() {
    color_echo "$BLUE" "[INFO] $*"
}

log_success() {
    color_echo "$GREEN" "[SUCCESS] $*"
}

log_warning() {
    color_echo "$YELLOW" "[WARNING] $*"
}

log_error() {
    color_echo "$RED" "[ERROR] $*"
}

# ----------------------------
# 工具函数
# ----------------------------
check_root_dir() {
    if [[ ! -d "$ROOT_DIR" ]]; then
        log_error "根目录不存在: $ROOT_DIR"
        exit 1
    fi
}

ensure_backup_dir() {
    if [[ ! -d "$BACKUP_DIR" ]]; then
        log_info "创建备份目录: $BACKUP_DIR"
        mkdir -p "$BACKUP_DIR"
    fi
}

get_pid() {
    if [[ -f "$PID_FILE" ]]; then
        local pid
        pid=$(cat "$PID_FILE" 2>/dev/null)
        if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then
            echo "$pid"
            return 0
        else
            # 清理无效的 PID 文件
            rm -f "$PID_FILE"
        fi
    fi
    echo ""
    return 1
}

wait_for_stop() {
    local pid=$1
    local timeout=${2:-10}  # 默认10秒超时
    local count=0

    while (( count < timeout )); do
        if ! kill -0 "$pid" 2>/dev/null; then
            return 0
        fi
        sleep 1
        ((count++))
    done
    return 1
}

# ----------------------------
# 备份函数
# ----------------------------
create_backup() {
    ensure_backup_dir

    local timestamp
    timestamp=$(date '+%Y%m%d_%H%M%S')
    local backup_name="${APP_NAME}_${timestamp}.bak"
    local backup_path="$BACKUP_DIR/$backup_name"

    if [[ ! -f "$APP_BIN" ]]; then
        log_warning "无可执行文件可备份: $APP_BIN"
        return 1
    fi

    log_info "创建备份: $backup_path"
    if cp "$APP_BIN" "$backup_path"; then
        log_success "备份创建成功: $backup_name"
        echo "$backup_path"
        return 0
    else
        log_error "备份创建失败"
        return 1
    fi
}

cleanup_old_backups() {
    ensure_backup_dir

    local backup_count
    backup_count=$(find "$BACKUP_DIR" -name "${APP_NAME}_*.bak" -type f 2>/dev/null | wc -l)

    if (( backup_count > MAX_BACKUPS )); then
        local files_to_delete
        files_to_delete=$((backup_count - MAX_BACKUPS))

        log_info "清理 $files_to_delete 个旧备份..."
        find "$BACKUP_DIR" -name "${APP_NAME}_*.bak" -type f 2>/dev/null | sort | head -n "$files_to_delete" | while read -r backup; do
            log_info "删除旧备份: $(basename "$backup")"
            rm -f "$backup"
        done
    fi
}

list_backups() {
    ensure_backup_dir

    local backups
    backups=$(find "$BACKUP_DIR" -name "${APP_NAME}_*.bak" -type f 2>/dev/null | sort -r)

    if [[ -z "$backups" ]]; then
        log_warning "没有找到备份文件"
        return 1
    fi

    color_echo "$CYAN" "可用的备份文件:"
    local count=0
    while IFS= read -r backup; do
        ((count++))
        local size
        size=$(du -h "$backup" 2>/dev/null | cut -f1 || echo "未知")
        local mtime
        mtime=$(stat -c %y "$backup" 2>/dev/null || stat -f %Sm "$backup" 2>/dev/null || echo "未知时间")
        echo "  $count. $(basename "$backup") (${size}, ${mtime})"
    done <<< "$backups"

    echo "$backups"
}

# ----------------------------
# 健康检查函数
# ----------------------------
health_check() {
    local pid=$1
    local timeout=${2:-$HEALTH_CHECK_TIMEOUT}
    local interval=${3:-$HEALTH_CHECK_INTERVAL}
    local count=0
    local max_checks=$((timeout / interval))

    log_info "进行健康检查 (超时: ${timeout}s, 间隔: ${interval}s)..."

    while (( count < max_checks )); do
        # 检查进程是否存活
        if ! kill -0 "$pid" 2>/dev/null; then
            log_error "进程已退出，启动失败"
            return 1
        fi

        # 这里可以添加应用特定的健康检查
        # 例如:
        # if curl -f -s http://localhost:8080/health >/dev/null 2>&1; then
        #     log_success "健康检查通过"
        #     return 0
        # fi

        # 临时健康检查：如果进程还在运行，暂时认为健康
        # 在实际使用中应该替换为具体的健康检查逻辑
        if kill -0 "$pid" 2>/dev/null; then
            log_success "健康检查通过（进程运行中）"
            return 0
        fi

        sleep "$interval"
        ((count++))
        log_info "健康检查中... ($((count * interval))s)"
    done

    log_error "健康检查超时"
    return 1
}

# ----------------------------
# 回滚函数
# ----------------------------
rollback() {
    local backup_choice=${1:-"latest"}

    log_info "开始回滚操作..."

    # 获取当前运行状态
    local current_pid
    current_pid=$(get_pid)

    # 列出可用的备份
    local backups
    backups=$(list_backups)

    if [[ $? -ne 0 ]]; then
        log_error "没有可用的备份用于回滚"
        return 1
    fi

    local backup_path
    if [[ "$backup_choice" == "latest" ]]; then
        backup_path=$(echo "$backups" | head -n1)
    else
        # 选择特定备份
        local backup_array=()
        while IFS= read -r line; do
            backup_array+=("$line")
        done <<< "$backups"

        if [[ "$backup_choice" =~ ^[0-9]+$ ]] && (( backup_choice > 0 && backup_choice <= ${#backup_array[@]} )); then
            backup_path="${backup_array[$((backup_choice-1))]}"
        else
            log_error "无效的备份选择: $backup_choice"
            return 1
        fi
    fi

    if [[ ! -f "$backup_path" ]]; then
        log_error "备份文件不存在: $backup_path"
        return 1
    fi

    log_info "选择回滚备份: $(basename "$backup_path")"

    # 备份当前版本（如果存在）
    if [[ -f "$APP_BIN" ]]; then
        log_info "备份当前版本..."
        if ! create_backup > /dev/null 2>&1; then
            log_warning "当前版本备份失败，继续回滚..."
        fi
    fi

    # 停止当前服务
    if [[ -n "$current_pid" ]]; then
        log_info "停止当前服务..."
        if ! stop > /dev/null 2>&1; then
            log_warning "停止服务失败，尝试继续回滚..."
        fi
        sleep 2
    fi

    # 执行回滚
    log_info "恢复备份文件..."
    if cp "$backup_path" "$APP_BIN" && chmod +x "$APP_BIN"; then
        log_success "回滚文件恢复成功"
    else
        log_error "回滚文件恢复失败"
        return 1
    fi

    # 重新启动服务
    log_info "启动回滚后的服务..."
    if start "background" > /dev/null 2>&1; then
        local new_pid
        new_pid=$(get_pid)
        if [[ -n "$new_pid" ]]; then
            log_success "回滚成功完成！"
            log_success "当前运行版本: $(basename "$backup_path")"
            log_success "服务PID: $new_pid"
            return 0
        else
            log_error "回滚后服务启动但未获取到PID"
            return 1
        fi
    else
        log_error "回滚后启动失败"
        return 1
    fi
}

# ----------------------------
# 安全启动函数（带错误回滚）
# ----------------------------
safe_start() {
    local mode=${1:-"background"}
    local backup_path=""

    # 在启动前创建备份（如果是部署新版本后的第一次启动）
    if [[ "$2" == "create_backup" ]]; then
        backup_path=$(create_backup)
    fi

    log_info "安全启动 $APP_NAME ..."

    if [[ ! -f "$APP_BIN" ]]; then
        log_error "可执行文件不存在: $APP_BIN"
        return 1
    fi

    if [[ ! -x "$APP_BIN" ]]; then
        chmod +x "$APP_BIN"
    fi

    local existing_pid
    existing_pid=$(get_pid)
    if [[ -n "$existing_pid" ]]; then
        log_warning "$APP_NAME 已在运行，PID=$existing_pid"
        return 1
    fi

    # 启动服务
    if [[ "$mode" == "foreground" ]]; then
        log_success "启动成功(前台模式)"
        exec "$APP_BIN" -c "$APP_CONF"
    else
        setsid nohup "$APP_BIN" -c "$APP_CONF" >> "$LOG_FILE" 2>&1 &
        local pid=$!
        echo "$pid" > "$PID_FILE"

        sleep 2
        if kill -0 "$pid" 2>/dev/null; then
            log_success "启动成功(后台模式)：PID=$pid"

            # 健康检查
            if health_check "$pid"; then
                log_success "服务启动完全成功"
                return 0
            else
                log_error "服务健康检查失败，执行回滚..."
                # 停止不健康的服务
                kill "$pid" 2>/dev/null || true
                rm -f "$PID_FILE"

                # 如果有备份则回滚
                if [[ -n "$backup_path" && -f "$backup_path" ]]; then
                    log_info "尝试回滚到之前版本..."
                    if cp "$backup_path" "$APP_BIN" && chmod +x "$APP_BIN"; then
                        log_success "回滚成功，尝试启动旧版本..."
                        safe_start "background"
                    else
                        log_error "回滚失败"
                        return 1
                    fi
                else
                    log_error "无可用备份，无法回滚"
                    return 1
                fi
            fi
        else
            log_error "启动失败，进程已退出"
            rm -f "$PID_FILE"

            # 如果有备份则回滚
            if [[ -n "$backup_path" && -f "$backup_path" ]]; then
                log_info "尝试回滚到之前版本..."
                if cp "$backup_path" "$APP_BIN" && chmod +x "$APP_BIN"; then
                    log_success "回滚成功，尝试启动旧版本..."
                    safe_start "background"
                else
                    log_error "回滚失败"
                    return 1
                fi
            else
                return 1
            fi
        fi
    fi
}

# ----------------------------
# 部署函数（带自动回滚）
# ----------------------------
deploy() {
    local new_binary=$1
    local enable_auto_rollback=${2:-true}

    if [[ ! -f "$new_binary" ]]; then
        log_error "新版本文件不存在: $new_binary"
        return 1
    fi

    log_info "开始部署新版本: $(basename "$new_binary")"

    # 创建备份
    local backup_path
    backup_path=$(create_backup)
    if [[ $? -ne 0 ]]; then
        if [[ "$enable_auto_rollback" == true ]]; then
            log_error "备份创建失败，无法确保回滚安全性，中止部署"
            return 1
        else
            log_warning "备份创建失败，继续部署（无回滚保障）"
        fi
    fi

    # 获取当前运行状态
    local current_pid
    current_pid=$(get_pid)
    local was_running=false
    if [[ -n "$current_pid" ]]; then
        was_running=true
        log_info "当前服务运行中，PID=$current_pid"
    fi

    # 停止当前服务
    if [[ "$was_running" == true ]]; then
        log_info "停止当前服务..."
        if ! stop; then
            log_error "停止当前服务失败，中止部署"
            return 1
        fi
        sleep 2
    fi

    # 部署新版本
    log_info "部署新版本文件..."
    if cp "$new_binary" "$APP_BIN" && chmod +x "$APP_BIN"; then
        log_success "新版本文件部署成功"
    else
        log_error "新版本文件部署失败"
        # 立即回滚
        if [[ "$enable_auto_rollback" == true && -n "$backup_path" && -f "$backup_path" ]]; then
            log_info "执行自动回滚..."
            if cp "$backup_path" "$APP_BIN" && chmod +x "$APP_BIN"; then
                log_success "自动回滚成功"
                # 重新启动原服务
                if [[ "$was_running" == true ]]; then
                    start > /dev/null 2>&1 && log_success "原服务重新启动成功"
                fi
            else
                log_error "自动回滚失败！系统处于不一致状态"
            fi
        fi
        return 1
    fi

    # 启动新服务（仅当之前服务在运行）
    if [[ "$was_running" == true ]]; then
        log_info "启动新版本服务..."
        if safe_start "background" "no_backup"; then
            log_success "部署成功完成！"
            cleanup_old_backups
            return 0
        else
            log_error "新版本启动失败"

            # 自动回滚
            if [[ "$enable_auto_rollback" == true && -n "$backup_path" && -f "$backup_path" ]]; then
                log_info "执行自动回滚..."
                if rollback "latest"; then
                    log_success "自动回滚成功"
                    return 1
                else
                    log_error "自动回滚失败！系统处于不一致状态"
                    return 1
                fi
            else
                log_error "自动回滚已禁用或无备份可用"
                return 1
            fi
        fi
    else
        log_success "部署完成（服务未启动，使用 start 命令手动启动）"
        cleanup_old_backups
        return 0
    fi
}

# ----------------------------
# 启动函数
# ----------------------------
start() {
    check_root_dir

    if [[ ! -f "$APP_BIN" ]]; then
        log_error "可执行文件不存在: $APP_BIN"
        exit 1
    fi

    if [[ ! -f "$APP_CONF" ]]; then
        log_warning "配置文件不存在: $APP_CONF"
    fi

    local existing_pid
    existing_pid=$(get_pid)
    if [[ -n "$existing_pid" ]]; then
        log_warning "$APP_NAME 已在运行，PID=$existing_pid"
        exit 1
    fi

    # 使用安全启动
    if ! safe_start "${1:-background}" "no_backup"; then
        log_error "启动失败"
        exit 1
    fi
}

# ----------------------------
# 停止函数
# ----------------------------
stop() {
    check_root_dir

    local pid
    pid=$(get_pid)

    if [[ -z "$pid" ]]; then
        log_warning "$APP_NAME 未运行"
        return 0
    fi

    log_info "停止 $APP_NAME (PID=$pid)..."
    if kill "$pid"; then
        if wait_for_stop "$pid" 10; then
            log_success "已正常停止"
            rm -f "$PID_FILE"
            return 0
        else
            log_warning "进程未正常退出，尝试强制杀死..."
            if kill -9 "$pid" 2>/dev/null; then
                sleep 1
                if kill -0 "$pid" 2>/dev/null; then
                    log_error "无法停止进程 $pid"
                    return 1
                else
                    log_success "已强制停止"
                    rm -f "$PID_FILE"
                    return 0
                fi
            else
                log_error "强制停止失败"
                return 1
            fi
        fi
    else
        log_error "发送停止信号失败"
        return 1
    fi
}

# ----------------------------
# 状态函数
# ----------------------------
status() {
    check_root_dir

    local pid
    pid=$(get_pid)

    if [[ -n "$pid" ]]; then
        log_success "$APP_NAME 正在运行, PID=$pid"
        if ps -p "$pid" >/dev/null 2>&1; then
            echo "进程信息:"
            ps -p "$pid" -o pid,ppid,user,%cpu,%mem,etime,cmd
        fi
        return 0
    else
        log_error "$APP_NAME 未运行"
        return 1
    fi
}

# ----------------------------
# 查看日志函数
# ----------------------------
show_log() {
    check_root_dir

    if [[ ! -f "$LOG_FILE" ]]; then
        log_error "日志文件不存在: $LOG_FILE"
        exit 1
    fi

    local lines=${1:-50}
    log_info "显示最后 $lines 行日志: $LOG_FILE"
    tail -f -n "$lines" "$LOG_FILE"
}

# ----------------------------
# 主入口
# ----------------------------
main() {
    case "$1" in
        start)
            start "$2"
            ;;
        stop)
            stop
            ;;
        restart)
            log_info "重启服务..."
            stop
            sleep 2
            start "$2"
            ;;
        status)
            status
            ;;
        log)
            show_log "$2"
            ;;
        backup)
            create_backup
            cleanup_old_backups
            ;;
        rollback)
            rollback "$2"
            ;;
        deploy)
            if [[ -z "$2" ]]; then
                log_error "请指定要部署的新版本文件路径"
                exit 1
            fi
            deploy "$2" "$3"
            ;;
        list-backups)
            list_backups
            ;;
        *)
            cat << EOF
用法: $0 {start|stop|restart|status|log|backup|rollback|deploy|list-backups} [options]

命令:
  start [foreground]       启动服务（前台或后台模式）
  stop                    停止服务
  restart [foreground]    重启服务
  status                  查看服务状态
  log [lines]             查看日志，默认显示最后50行
  backup                  创建备份
  rollback [number|latest]回滚到指定备份（默认最新）
  deploy <new_binary>     部署新版本（带自动回滚）
  list-backups            列出所有备份

自动回滚策略:
  • 部署失败时自动回滚
  • 启动后健康检查失败时自动回滚
  • 进程异常退出时自动回滚

示例:
  $0 deploy /path/to/new_binary    # 部署新版本（自动回滚）
  $0 rollback                     # 回滚到最新备份
  $0 start foreground             # 前台启动（调试）

文件位置:
  根目录:    $ROOT_DIR
  备份目录:  $BACKUP_DIR
EOF
            exit 1
            ;;
    esac
}

# 运行主函数
main "$@"